package ${packageName};

<#list cacheImports as imports>
import ${imports};
</#list>
/**
 * ${tableName}
 */
public class ${d_tableName}Cache {
	private static AtomicInteger LASTID = new AtomicInteger();
	private static boolean isLoadAll = false;
	
	public static boolean isLoadAll() {
		return isLoadAll;
	}

	public static void setLoadAll(boolean isLoadAll) {
		${d_tableName}Cache.isLoadAll = isLoadAll;
	}
	//缓存
	<#list indexKeys as index>
	<#if index.unique>
	<#if index.indexName == "PRIMARY">
	static final ConcurrentSkipListMap<String, ${d_tableName}> <#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache = new ConcurrentSkipListMap<String, ${d_tableName}>();
	<#else>
	static final ConcurrentHashMap<String, String> <#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache = new ConcurrentHashMap<String, String>();
	</#if>
	<#else>
	static final ConcurrentHashMap<String, CopyOnWriteArraySet<String>> <#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache = new ConcurrentHashMap<String, CopyOnWriteArraySet<String>>();
	</#if>
	</#list>
	// 被释放对象
	<#list indexKeys as index>
	<#if !index.unique>
	static final ConcurrentHashMap<String, CopyOnWriteArraySet<String>> release_<#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache = new ConcurrentHashMap<String, CopyOnWriteArraySet<String>>();
	</#if>
	</#list>
	// 待入库集合
	static final Map<String, ${d_tableName}> saveDelete = new HashMap<String, ${d_tableName}>();
	static final Map<String, ${d_tableName}> saveInsert = new HashMap<String, ${d_tableName}>();
	static final Map<String, ${d_tableName}> saveUpdate = new HashMap<String, ${d_tableName}>();
	// 数据线程
	static ExecutorService saveThreadPool = Executors.newSingleThreadExecutor();
	//表锁(重入锁)
	static final ReentrantLock tableLock = new ReentrantLock();
	
	private static ${d_tableName} getClone(String key){
		<#list indexKeys as index>
		<#if index.indexName == "PRIMARY">
		${d_tableName} ${x_tableName} = <#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.get(key);
		</#if>
		</#list>
		if(${x_tableName}!=null)
			${x_tableName} = ${x_tableName}.clone();
		return ${x_tableName};
	}
	
	<#list indexKeys as index>
	/**
	 * 根据(<#list index.columnNames as columnName> ${columnName} </#list>) 查询
	 */
	<#if index.unique>
	<#-- 唯一索引查询 -->
	public static ${d_tableName} getBy<#list index.d_columnNames as d_columnName>${d_columnName}</#list>(${index.all_basicType_x_columnName}){
		<#if index.indexName == "PRIMARY">
		${d_tableName} ${x_tableName} = null;
		String key = ${index.all_d_columnName_plus};
		${x_tableName} = getClone(key);
		if(${x_tableName}==null){
			<#if db.mode == "cache_redis">
			${x_tableName} = ${d_tableName}Jedis.getBy<#list index.d_columnNames as d_columnName>${d_columnName}</#list>(${index.all_x_columnName});
			<#elseif db.mode == "cache_mysql">
			${x_tableName} = ${d_tableName}Dao.getBy<#list index.d_columnNames as d_columnName>${d_columnName}</#list>(${index.all_x_columnName});
			</#if>	
			if(${x_tableName}!=null){
				try {
					${x_tableName} = loadCache(${x_tableName},null,SK_Config.NO_CHECK_INDEX);
				} catch (SK_Exception e) {
					e.printStackTrace();
				}
			}
		}
		return ${x_tableName};
		<#else>
		${d_tableName} ${x_tableName} = null;
		String key = ${index.all_d_columnName_plus};
		key = <#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.get(key);
		if(key!=null){
			${x_tableName} = getBy${primaryD_columnName}(Integer.valueOf(key));	
		} 
		if(${x_tableName}==null){
			<#if db.mode == "cache_redis">
			${x_tableName} = ${d_tableName}Jedis.getBy<#list index.d_columnNames as d_columnName>${d_columnName}</#list>(${index.all_x_columnName});
			<#elseif db.mode == "cache_mysql">
			${x_tableName} = ${d_tableName}Dao.getBy<#list index.d_columnNames as d_columnName>${d_columnName}</#list>(${index.all_x_columnName});
			</#if>	
			if(${x_tableName}!=null){
				try {
					${x_tableName} = loadCache(${x_tableName},null,SK_Config.NO_CHECK_INDEX);
				} catch (SK_Exception e) {
					e.printStackTrace();
				}
			}
		}
		return ${x_tableName};
		</#if>
	}
	
	<#else>
	<#-- 聚集索引查询 -->	
	public static List<${d_tableName}> getBy<#list index.d_columnNames as d_columnName>${d_columnName}</#list>(${index.all_basicType_x_columnName}){
		List<${d_tableName}> ${x_tableName}s = new ArrayList<${d_tableName}>();
		final String key = ${index.all_d_columnName_plus};
		CopyOnWriteArraySet<String> keys = <#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.get(key);
		if(keys != null){
			Future<List<${d_tableName}>> futures = null;
			final CopyOnWriteArraySet<String> release_keys = release_<#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.get(key);
			if (release_keys!=null && !release_keys.isEmpty()) {
				futures = SK_Config.getInThreadPool().submit(new Callable<List<${d_tableName}>>() {
					public List<${d_tableName}> call() throws Exception {
							Object[] release_keys_arrays = release_keys.toArray();
							${primaryBasicType}[] basicTypekeys = new ${primaryBasicType}[release_keys_arrays.length];
							for (int i = 0; i < release_keys_arrays.length; i++) {
								basicTypekeys[i] = ${primaryClassType}.valueOf(release_keys_arrays[i].toString());
							}
							<#if db.mode == "cache_redis">
							return ${d_tableName}Jedis.getBy${primaryD_columnName}In(basicTypekeys);
							<#elseif db.mode == "cache_mysql">
							return ${d_tableName}Dao.getBy${primaryD_columnName}In(basicTypekeys);
							</#if>
						}
					});
			}
			${d_tableName} ${x_tableName} = null;
			for (String k : keys) {
				${x_tableName} = getBy${primaryD_columnName}(Integer.valueOf(k));
				if (${x_tableName} == null) continue;
					${x_tableName}s.add(${x_tableName});
			}
			if(futures!=null){
				List<${d_tableName}> futureObjs = null;
				try {
					futureObjs = futures.get();
				} catch (Exception e) {
					e.printStackTrace();
				}
				if(futureObjs !=null){
					${x_tableName}s.addAll(loadCaches(futureObjs,${d_tableName}.<#list index.d_columnNames as d_columnName>${d_columnName}</#list>_IndexName,SK_Config.CHECK_ALL_INDEX));
				}
			}
		}else{
			<#if db.mode == "cache_redis">
			${x_tableName}s = ${d_tableName}Jedis.getBy<#list index.d_columnNames as d_columnName>${d_columnName}</#list>(${index.all_x_columnName});
			<#elseif db.mode == "cache_mysql">
			${x_tableName}s = ${d_tableName}Dao.getBy<#list index.d_columnNames as d_columnName>${d_columnName}</#list>(${index.all_x_columnName});
			</#if>
			if(!${x_tableName}s.isEmpty()){
				${x_tableName}s = loadCaches(${x_tableName}s,${d_tableName}.<#list index.d_columnNames as d_columnName>${d_columnName}</#list>_IndexName,SK_Config.NO_CHECK_INDEX);
			}
		}
		return ${x_tableName}s;
	}
	
	<#-- 聚集索引分页查询 -->
	public static List<${d_tableName}> getByPage<#list index.d_columnNames as d_columnName>${d_columnName}</#list>(${index.all_basicType_x_columnName},int page,int size,SK_PageCount pageCount){
		List<${d_tableName}> ${x_tableName}s = getBy<#list index.d_columnNames as d_columnName>${d_columnName}</#list>(${index.all_x_columnName});
		${x_tableName}s = SK_List.getPage(${x_tableName}s, page, size, pageCount);
		return ${x_tableName}s;
	}
	</#if>
	</#list>
	
	/**
	 *  非线程安全 & 无锁  (慎用)
	 */
	public static List<${d_tableName}> getCacheAll(){
		<#list indexKeys as index>
		<#if index.indexName == "PRIMARY">
		List<${d_tableName}> ${x_tableName}s = new ArrayList<${d_tableName}>();
		Collection<${d_tableName}> objs = <#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.values();
		for(${d_tableName} obj : objs){
			${x_tableName}s.add(obj.clone());
		}
		return ${x_tableName}s;
		</#if>
		</#list>
	}
	
	
	/**
	 * 全部加载进内存 & 非线程安全 & 无锁  (慎用)
	 */
	public static List<${d_tableName}> getAll(){
		List<${d_tableName}> ${x_tableName}s = new ArrayList<${d_tableName}>();
		if(!isLoadAll){
			<#if db.mode == "cache_redis">
			${x_tableName}s = ${d_tableName}Jedis.getAll();
			<#elseif db.mode == "cache_mysql">
			${x_tableName}s = ${d_tableName}Dao.getAll();
			</#if>
			${x_tableName}s = loadCaches(${x_tableName}s,null,SK_Config.NO_CHECK_INDEX);
			isLoadAll = true;
		}else{
			${x_tableName}s = getCacheAll();
		}
		return ${x_tableName}s;
	}
	
	
	/**
	 * 全部加载进内存 并 锁住全部对象 (慎用)
	 */
	public static List<${d_tableName}> getAllByPage(int page,int size,SK_PageCount pageCount) {
		List<${d_tableName}> ${x_tableName}s = getAll();
		${x_tableName}s = SK_List.getPage(${x_tableName}s, page, size, pageCount);
		return ${x_tableName}s;
	}
	
	/**
	 * 批量加载缓存(内部会进行一次克隆)
	 */
	private static List<${d_tableName}> loadCaches(List<${d_tableName}> ${x_tableName}s,String indexName,int type){
		List<${d_tableName}> ${x_tableName}s_clone = new ArrayList<${d_tableName}>();
		${d_tableName} ${x_tableName}_ = null; 
		for(${d_tableName} ${x_tableName} : ${x_tableName}s){
			 try {
					${x_tableName}_ = loadCache(${x_tableName},indexName,type);
					if(${x_tableName}_!=null)
						${x_tableName}s_clone.add(${x_tableName}_);
			 } catch (SK_Exception e) {
				e.printStackTrace();
			}
		}
		return ${x_tableName}s_clone;
	}
	
	/**
	 * 加载缓存
	 * 1.验证索引就会加表锁)
	 * 2.return的对象是clone对象
	 * 3.返回值可能为 null
	 * type : 1.检查全部索引  2.检查变动索引  3.不检查索引
	 */
	private static ${d_tableName} loadCache(${d_tableName} ${x_tableName},String indexName,int type) throws SK_Exception{
		boolean isLock = (type == SK_Config.CHECK_ALL_INDEX);
		Map<String,String> updateIndexs = null;
		if(type == SK_Config.CHECK_UPDATE_INDEX){
			updateIndexs = ${x_tableName}.getUpdateIndexs();
			isLock = updateIndexs.containsKey("isUpdtaeUnique");
		}
		try{
			DbLock.lock(${x_tableName}.get${primaryD_columnName}(),DbLock.${x_tableName}_locks);
			if(isLock)tableLock.lock();
			switch (type) {
			case SK_Config.CHECK_ALL_INDEX:
				checkUniqueIndex(${x_tableName});
				break;
			case SK_Config.CHECK_UPDATE_INDEX:
				checkUniqueIndex(updateIndexs);
				break;
			}
			if(type == SK_Config.CHECK_UPDATE_INDEX){
				clearCache(${x_tableName},false);
			}
			<#assign i=0 />
			<#list indexKeys as index>
			<#if index.unique>
			<#if index.indexName == "PRIMARY">
			//内存对象上的索引更新
			${d_tableName} ${x_tableName}_clone = ${x_tableName}.clone();
			//避免重复加载
			if(${x_tableName}_clone._getVer()==${x_tableName}._getVer() && type == SK_Config.NO_CHECK_INDEX){
				return ${x_tableName}_clone;
			}
			if(type == SK_Config.CHECK_UPDATE_INDEX)
				${x_tableName}_clone._nextVer();
			<#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.put(${index.all_d_columnName_get},${x_tableName}_clone);
			
			<#else>
			<#if i==0>
			String field = ${index.all_d_columnName_get};
			<#assign i=1 />
			<#else>
			field = ${index.all_d_columnName_get};
			</#if>
			<#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.put(field,String.valueOf(${x_tableName}.get${primaryD_columnName}()));
			
			</#if>
			<#else>
			CopyOnWriteArraySet<String> <#list index.x_columnNames as x_columnName>${x_columnName}</#list>set = <#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.get(String.valueOf(${index.all_d_columnName_get}));
			if(<#list index.x_columnNames as x_columnName>${x_columnName}</#list>set == null && ${d_tableName}.<#list index.d_columnNames as d_columnName>${d_columnName}</#list>_IndexName.equals(indexName)){
				<#list index.x_columnNames as x_columnName>${x_columnName}</#list>set = new CopyOnWriteArraySet<String>();
				<#list index.x_columnNames as x_columnName>${x_columnName}</#list>set.add(String.valueOf(${x_tableName}.get${primaryD_columnName}()));
			}
			if(<#list index.x_columnNames as x_columnName>${x_columnName}</#list>set != null){
				<#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.put(${index.all_d_columnName_get},<#list index.x_columnNames as x_columnName>${x_columnName}</#list>set);
				release_<#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.remove(String.valueOf(${x_tableName}.get${primaryD_columnName}()));
			}
			</#if>
			</#list>
			
			return ${x_tableName}_clone;
		}finally{
			DbLock.unlock(${x_tableName}.get${primaryD_columnName}(),DbLock.${x_tableName}_locks);
			if(isLock)tableLock.unlock();
		}
	}
	
	
	
	/**
	 * 清空缓存 
	 */
	public static void clearCache(${d_tableName} ${x_tableName},boolean isRelease){
		try{
			DbLock.lock(${x_tableName}.get${primaryD_columnName}(),DbLock.${x_tableName}_locks);
			<#list indexKeys as index>
			<#if index.unique>
			<#if index.indexName == "PRIMARY">
			<#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.remove(${index.all_d_columnName_get});
			
			<#else>
			<#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.remove(${x_tableName}.get<#list index.d_columnNames as d_columnName>${d_columnName}</#list>_Unique());
			</#if>
			<#else>
			
			CopyOnWriteArraySet<String> <#list index.x_columnNames as x_columnName>${x_columnName}</#list>set = <#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.get(${x_tableName}.get<#list index.d_columnNames as d_columnName>${d_columnName}</#list>_Index());
			if(<#list index.x_columnNames as x_columnName>${x_columnName}</#list>set != null){
				<#list index.x_columnNames as x_columnName>${x_columnName}</#list>set.remove(String.valueOf(${x_tableName}.get${primaryD_columnName}()));
			}
			</#if>
			</#list>
			
			//清空缓存记录
			if(isRelease){		
			<#list indexKeys as index>
			<#if !index.unique>
				CopyOnWriteArraySet<String> release_<#list index.x_columnNames as x_columnName>${x_columnName}</#list>set = release_<#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.get(String.valueOf(${index.all_d_columnName_get}));
				if(release_<#list index.x_columnNames as x_columnName>${x_columnName}</#list>set == null){
					release_<#list index.x_columnNames as x_columnName>${x_columnName}</#list>set = new CopyOnWriteArraySet<String>();
					release_<#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.put(String.valueOf(${index.all_d_columnName_get}),release_<#list index.x_columnNames as x_columnName>${x_columnName}</#list>set);
				}
				release_<#list index.x_columnNames as x_columnName>${x_columnName}</#list>set.add(String.valueOf(${x_tableName}.get${primaryD_columnName}()));
				
			</#if>
			</#list>
			}
		}finally{
			DbLock.unlock(${x_tableName}.get${primaryD_columnName}(),DbLock.${x_tableName}_locks);
		}
	}
	
	/**
	 * 清空缓存
	 */
	public static void clearCaches(List<${d_tableName}> ${x_tableName}s){
		for(${d_tableName} ${x_tableName} : ${x_tableName}s){
			clearCache(${x_tableName},true);
		}
	}
	
	public static ${d_tableName} insert(${d_tableName} ${x_tableName}){
		return insert(${x_tableName},false);
    }
    
    public static ${d_tableName} update(${d_tableName} ${x_tableName}){
		return update(${x_tableName},false);
    }
    
    public static boolean delete(${d_tableName} ${x_tableName}){
		return delete(${x_tableName},false);
    }
    
    private static ${d_tableName} insert(${d_tableName} ${x_tableName},boolean isFlush){
    	synchronized (LASTID) {
			int id = LASTID.get();
	    	if(id < 1){
	    		<#if db.mode == "cache_redis">
				${x_tableName} = ${d_tableName}Jedis.insert(${x_tableName});
				<#elseif db.mode == "cache_mysql">
				${x_tableName} = ${d_tableName}Dao.insert(${x_tableName});
				</#if>
	    		LASTID.set(${x_tableName}.get${primaryD_columnName}());
	    		isFlush = true;
	    	}else{
	    		int _id = ${x_tableName}.get${primaryD_columnName}();
				if (_id < 1) {
					${x_tableName}.set${primaryD_columnName}(LASTID.incrementAndGet());
				} else if (${x_tableName}.get${primaryD_columnName}() > id) {
					LASTID.set(${x_tableName}.get${primaryD_columnName}());
				}
	    	}
		}
		try {
				DbLock.lock(${x_tableName}.get${primaryD_columnName}(),DbLock.${x_tableName}_locks);
		    	if(${x_tableName}!=null){
		    		try{
		    			${x_tableName} = loadCache(${x_tableName},null,SK_Config.CHECK_ALL_INDEX);
				    	if(!isFlush){
				    		final String key = String.valueOf(${x_tableName}.get${primaryD_columnName}());
				    		final ${d_tableName} save_${x_tableName} = ${x_tableName}.clone();
				    		saveThreadPool.execute(new Runnable() {
								public void run() {
									saveInsert.put(key,save_${x_tableName});
								}
							});				
						}
		    		 } catch (SK_Exception e) {
		    		 	e.printStackTrace();
		    		 	${x_tableName} = null;
		    		 }
				}
		}finally{
			DbLock.unlock(${x_tableName}.get${primaryD_columnName}(),DbLock.${x_tableName}_locks);
		}
    	return ${x_tableName};
    }
    
    private static ${d_tableName} update(${d_tableName} ${x_tableName},final boolean isFlush){
    	try {
    		DbLock.lock(${x_tableName}.get${primaryD_columnName}(),DbLock.${x_tableName}_locks);
	    	if(${x_tableName}.getUpdateColumns().isEmpty())return ${x_tableName};
	    	final String key = String.valueOf(${x_tableName}.get${primaryD_columnName}());
	    	${d_tableName} ${x_tableName}_ = remedy(${x_tableName}); 
	    	${x_tableName} = loadCache(${x_tableName}_,null,SK_Config.CHECK_UPDATE_INDEX);
	    	final ${d_tableName} save_${x_tableName} = ${x_tableName}.clone();
	    		saveThreadPool.execute(new Runnable() {
					public void run() {
						if(!isFlush){
							saveUpdate.put(key,save_${x_tableName});
						}else{
							saveUpdate.remove(key);
						}
					}
				});
		 } catch (SK_Exception e) {
		 	e.printStackTrace();
		 	${x_tableName} = null;
		 }finally{
			DbLock.unlock(${x_tableName}.get${primaryD_columnName}(),DbLock.${x_tableName}_locks);
		}
    	return ${x_tableName};
    }
    
    private static boolean delete(${d_tableName} ${x_tableName},final boolean isFlush){
    	try {
    		DbLock.lock(${x_tableName}.get${primaryD_columnName}(),DbLock.${x_tableName}_locks);
	    	clearCache(${x_tableName},false);
	    	final String key = String.valueOf(${x_tableName}.get${primaryD_columnName}());
	    	final ${d_tableName} save_${x_tableName} = ${x_tableName}.clone();
	    	saveThreadPool.execute(new Runnable() {
						public void run() {
							if(!isFlush){
								saveUpdate.remove(key);
								saveInsert.remove(key);
								saveDelete.put(key,save_${x_tableName});
					    	}else{
					    		saveUpdate.remove(key);
								saveInsert.remove(key);
								saveDelete.remove(key);
					    	}
					    }
					});	
		}finally{
			DbLock.unlock(${x_tableName}.get${primaryD_columnName}(),DbLock.${x_tableName}_locks);
		}
    	return false;
    }
    
    public static ${d_tableName} updateAndFlush(${d_tableName} ${x_tableName}){
    	if(${x_tableName}.getUpdateColumns().isEmpty())return ${x_tableName};
    	update(${x_tableName},true);
    	<#if db.mode == "cache_redis">
		return ${d_tableName}Jedis.update(${x_tableName});
		<#elseif db.mode == "cache_mysql">
		return ${d_tableName}Dao.update(${x_tableName});
		</#if>
    }
    
    public static ${d_tableName} insertAndFlush(${d_tableName} ${x_tableName}){
    	int id = LASTID.get();
    	insert(${x_tableName},true);
    	if(id > 0){
    		<#if db.mode == "cache_redis">
			${x_tableName} = ${d_tableName}Jedis.insert(${x_tableName});
			<#elseif db.mode == "cache_mysql">
			${x_tableName} = ${d_tableName}Dao.insert(${x_tableName});
			</#if>
    	}
    	return ${x_tableName};
    }
    
    public static boolean deleteAndFlush(${d_tableName} ${x_tableName}){
    	delete(${x_tableName},true);
    	<#if db.mode == "cache_redis">
		return ${d_tableName}Jedis.delete(${x_tableName});
		<#elseif db.mode == "cache_mysql">
		return ${d_tableName}Dao.delete(${x_tableName});
		</#if>
    }
    
    
    
    // ******************************** 持久化操作 ********************************
    <#if db.mode == "cache_redis">
    public static void saveRedis(){
    	saveThreadPool.execute(new Runnable() {
					public void run() {
    			saveRedis(true);
    		}
		});	
    }
    
    public static void saveRedis(final boolean isClear){
		saveRedis(new ArrayList<${d_tableName}>(saveDelete.values()),1,isClear);
		saveRedis(new ArrayList<${d_tableName}>(saveInsert.values()),2,isClear);
		saveRedis(new ArrayList<${d_tableName}>(saveUpdate.values()),3,isClear);
    }
    
    public static void saveRedis(${d_tableName} ${x_tableName}){
    	saveRedis(${x_tableName},true);
    }
    
    public static void saveRedis(final ${d_tableName} ${x_tableName},final boolean isClear){
    	final String key = String.valueOf(${x_tableName}.get${primaryD_columnName}());
    	saveThreadPool.execute(new Runnable() {
					public void run() {
						if (saveDelete.containsKey(key)){
							if(isClear)saveDelete.remove(key);
							${d_tableName}Jedis.delete(${x_tableName});
						}
						if (saveInsert.containsKey(key)){
							if(isClear)saveInsert.remove(key);
							${d_tableName}Jedis.insert(${x_tableName});
						}
						if (saveUpdate.containsKey(key)){
							if(isClear)saveUpdate.remove(key);
							${d_tableName}Jedis.update(${x_tableName});
						}
				    }
				});	
    }
    
    /* 0:all 1:delete 2:insert 3:update */
    public static void saveRedis(List<${d_tableName}> ${x_tableName}s,int type,final boolean isClear){
    	if(type == 1 || type == 0 ){
    		if(${d_tableName}Jedis.deleteBatch(${x_tableName}s) && isClear){
	    		final List<String> keys = new ArrayList<String>();
				for(${d_tableName} ${x_tableName} : ${x_tableName}s){
					keys.add(String.valueOf(${x_tableName}.get${primaryD_columnName}()));
				}
				saveThreadPool.execute(new Runnable() {
						public void run() {
							saveDelete.keySet().removeAll(keys);
						}
					});	
			}
    	}
		if(type == 2 || type == 0){
			if(!${d_tableName}Jedis.insertBatch(${x_tableName}s).isEmpty() && isClear){
				final List<String> keys = new ArrayList<String>();
				for(${d_tableName} ${x_tableName} : ${x_tableName}s){
					keys.add(String.valueOf(${x_tableName}.get${primaryD_columnName}()));
				}
				saveThreadPool.execute(new Runnable() {
						public void run() {
							saveInsert.keySet().removeAll(keys);
						}
					});	
			}
		}
		if(type == 3 || type == 0){
			if(!${d_tableName}Jedis.updateBatch(${x_tableName}s).isEmpty() && isClear){
				final List<String> keys = new ArrayList<String>();
				for(${d_tableName} ${x_tableName} : ${x_tableName}s){
					keys.add(String.valueOf(${x_tableName}.get${primaryD_columnName}()));
				}
				saveThreadPool.execute(new Runnable() {
						public void run() {
							saveUpdate.keySet().removeAll(keys);
						}
					});	
			}
		}
    }
    
    <#elseif db.mode == "cache_mysql">
    public static void saveDatabase(){
    	saveThreadPool.execute(new Runnable() {
					public void run() {
    			saveDatabase(true);
    		}
		});	
    }
    
    public static void saveDatabase(final boolean isClear){
		saveDatabase(new ArrayList<${d_tableName}>(saveDelete.values()),1,isClear);
    	saveDatabase(new ArrayList<${d_tableName}>(saveInsert.values()),2,isClear);
    	saveDatabase(new ArrayList<${d_tableName}>(saveUpdate.values()),3,isClear);
    }
    
    public static void saveDatabase(${d_tableName} ${x_tableName}){
    	saveDatabase(${x_tableName},true);
    }
    
    public static void saveDatabase(final ${d_tableName} ${x_tableName},final boolean isClear){
    	final String key = String.valueOf(${x_tableName}.get${primaryD_columnName}());
    	saveThreadPool.execute(new Runnable() {
					public void run() {
						if (!saveDelete.containsKey(key)){
							if(isClear)saveDelete.remove(key);
							${d_tableName}Dao.delete(${x_tableName});
						}
						if (!saveInsert.containsKey(key)){
							if(isClear)saveInsert.remove(key);
							${d_tableName}Dao.insert(${x_tableName});
						}
						if (!saveUpdate.containsKey(key)){
							if(isClear)saveUpdate.remove(key);
							${d_tableName}Dao.update(${x_tableName});
						}
				    }
				});	
    }
    
    /* 0:all 1:delete 2:insert 3:update */
    public static void saveDatabase(List<${d_tableName}> ${x_tableName}s,int type,final boolean isClear){
    	if(type == 1 || type == 0 ){
    		if(${d_tableName}Dao.deleteBatch(${x_tableName}s) && isClear){
    			final List<String> keys = new ArrayList<String>();
    			for(${d_tableName} ${x_tableName} : ${x_tableName}s){
    				keys.add(String.valueOf(${x_tableName}.get${primaryD_columnName}()));
    			}
    			saveThreadPool.execute(new Runnable() {
						public void run() {
							saveDelete.keySet().removeAll(keys);
						}
					});	
    		}
    	}
		
		if(type == 2 || type == 0 ){
			if(${d_tableName}Dao.insertBatch(${x_tableName}s).length > 0 && isClear){
				final List<String> keys = new ArrayList<String>();
    			for(${d_tableName} ${x_tableName} : ${x_tableName}s){
    				keys.add(String.valueOf(${x_tableName}.get${primaryD_columnName}()));
    			}
    			saveThreadPool.execute(new Runnable() {
						public void run() {
							saveInsert.keySet().removeAll(keys);
						}
					});	
			}
		}
		
		if(type == 3 || type == 0 ){
			if(${d_tableName}Dao.updateBatch(${x_tableName}s).length > 0 && isClear){
				final List<String> keys = new ArrayList<String>();
    			for(${d_tableName} ${x_tableName} : ${x_tableName}s){
    				keys.add(String.valueOf(${x_tableName}.get${primaryD_columnName}()));
    			}
    			saveThreadPool.execute(new Runnable() {
						public void run() {
							saveUpdate.keySet().removeAll(keys);
						}
					});
			}
		}
    }
    
    </#if>
    public static void saveAll(){
    	saveThreadPool.execute(new Runnable() {
    		public void run() {
				<#if db.mode == "cache_redis">
				saveRedis(false);
				<#elseif db.mode == "cache_mysql">
		   		saveDatabase(false);
				</#if>
		   		saveDelete.clear();
		    	saveInsert.clear();
		    	saveUpdate.clear();
		   	}
		});	
    }
    
    public static void saveAll(final ${d_tableName} ${x_tableName}){
    	saveThreadPool.execute(new Runnable() {
    		public void run() {
				<#if db.mode == "cache_redis">
				saveRedis(${x_tableName},false);
				<#elseif db.mode == "cache_mysql">
		   		saveDatabase(${x_tableName},false);
				</#if>
				final String key = String.valueOf(${x_tableName}.get${primaryD_columnName}());
				saveDelete.remove(key);
		    	saveInsert.remove(key);
		    	saveUpdate.remove(key);
			}
		});	
    }
    
    public static void saveAll(final List<${d_tableName}> ${x_tableName}s){
    	saveThreadPool.execute(new Runnable() {
    		public void run() {
				<#if db.mode == "cache_redis">
				saveRedis(${x_tableName}s,0,false);
				<#elseif db.mode == "cache_mysql">
		   		saveDatabase(${x_tableName}s,0,false);
				</#if>
				final List<String> keys = new ArrayList<String>();
				for(${d_tableName} ${x_tableName} : ${x_tableName}s){
					keys.add(String.valueOf(${x_tableName}.get${primaryD_columnName}()));
				}
				saveDelete.keySet().removeAll(keys);
		    	saveInsert.keySet().removeAll(keys);
		    	saveUpdate.keySet().removeAll(keys);
			}
		});	
    }
    // ******************************** 级联操作 ********************************
    
    /**
	 * 级联加载缓存
	 */
	public static void loadCacheCascade(${d_tableName} ${x_tableName}){
		try{
			loadCache(${x_tableName},null,SK_Config.NO_CHECK_INDEX);
		} catch (SK_Exception e) {
    		 	e.printStackTrace();
    	}
		<#list bindKeys as bindKey>
		<#if !bindKey.pk>
		<#if bindKey.unique>
		<#-- 1对1 -->
		${bindKey.d_fkTableName}Imp.loadCacheCascade(${bindKey.d_fkTableName}Imp.getBy${bindKey.d_fkColumnName}(${x_tableName}.get${bindKey.d_pkColumnName}()));
		<#else>
		<#-- 1对多 -->
		${bindKey.d_fkTableName}Imp.loadCachesCascade(${bindKey.d_fkTableName}Imp.getBy${bindKey.d_fkColumnName}(${x_tableName}.get${bindKey.d_pkColumnName}()));
		</#if>
		</#if>
		</#list>
	}
	
	/**
	 * 级联加载缓存
	 */
	public static void loadCachesCascade(List<${d_tableName}> ${x_tableName}s){
		for(${d_tableName} ${x_tableName} : ${x_tableName}s){
			loadCacheCascade(${x_tableName});
		}
	}
	
	
	/**
	 * 级联清除缓存
	 */
	public static void clearCacheCascade(${d_tableName} ${x_tableName}){
	
		clearCache(${x_tableName},true);
		
		<#list bindKeys as bindKey>
		<#if !bindKey.pk>
		<#if bindKey.unique>
		<#-- 1对1 -->
		${bindKey.d_fkTableName}Imp.clearCacheCascade(${bindKey.d_fkTableName}Imp.getBy${bindKey.d_fkColumnName}(${x_tableName}.get${bindKey.d_pkColumnName}()));
		<#else>
		<#-- 1对多 -->
		${bindKey.d_fkTableName}Imp.clearCachesCascade(${bindKey.d_fkTableName}Imp.getBy${bindKey.d_fkColumnName}(${x_tableName}.get${bindKey.d_pkColumnName}()));
		</#if>
		</#if>
		</#list>
	}
	
	/**
	 * 级联清除缓存
	 */
	public static void clearCachesCascade(List<${d_tableName}> ${x_tableName}s){
		for(${d_tableName} ${x_tableName} : ${x_tableName}s){
			clearCacheCascade(${x_tableName});
		}
		<#if db.mode == "cache_redis">
		saveRedis(${x_tableName}s,0,true);
		<#elseif db.mode == "cache_mysql">
		saveDatabase(${x_tableName}s,0,true);
		</#if>
	}
	
	// 数据补救
	private static ${d_tableName} remedy(${d_tableName} ${x_tableName}){
		<#list indexKeys as index>
		<#if index.indexName == "PRIMARY">
		${d_tableName} _${x_tableName} = getClone(String.valueOf(${x_tableName}.get${primaryD_columnName}()));
		if(_${x_tableName}!=null){
			_${x_tableName}.set_source(true);
		}
		</#if>
		</#list>
		if(_${x_tableName}==null || ${x_tableName} == null)return ${x_tableName};
		//检查版本号
		if(_${x_tableName}._getVer() == ${x_tableName}._getVer())return ${x_tableName};
		SK_ByteBuff byteBuff = ${x_tableName}.getSK_ByteBuff();
		while(byteBuff.isRead()){				
			switch (byteBuff.readInt(null)) {
			<#assign i=0 />
			<#list columns as column>
			<#assign i=i+1 />
			case ${i}:
				<#if column.basicType=="Date" || column.basicType=="java.util.Date">
				_${x_tableName}.set${column.d_columnName}(byteBuff.readDate(null));
				<#else>
				_${x_tableName}.set${column.d_columnName}(byteBuff.read${column.basicType?cap_first}(null));
				</#if>
				break;
			<#if column.basicType=="short" || column.basicType=="int" || column.basicType=="long" || column.basicType=="float" || column.basicType=="double">
			<#assign i=i+1 />
			case ${i}:
				_${x_tableName}.change${column.d_columnName}With(byteBuff.read${column.basicType?cap_first}(null));
				break;
			<#assign i=i+1 />
			case ${i}:
				_${x_tableName}.change${column.d_columnName}WithMin(byteBuff.read${column.basicType?cap_first}(null),byteBuff.read${column.basicType?cap_first}(null));
				break;
			<#assign i=i+1 />
			case ${i}:
				_${x_tableName}.change${column.d_columnName}WithMax(byteBuff.read${column.basicType?cap_first}(null),byteBuff.read${column.basicType?cap_first}(null));
				break;
			<#assign i=i+1 />
			case ${i}:
				_${x_tableName}.change${column.d_columnName}WithMaxMin(byteBuff.read${column.basicType?cap_first}(null),byteBuff.read${column.basicType?cap_first}(null),byteBuff.read${column.basicType?cap_first}(null));
				break;
			</#if>
			</#list>
			}
		}
		${x_tableName} = _${x_tableName}; 
		return ${x_tableName};
	}
	
	//检查唯一索引
	private static void checkUniqueIndex(${d_tableName} ${x_tableName}) throws SK_Exception{
		try{
			tableLock.lock();
			<#assign i=0 />
			<#list indexKeys as index>
			<#if index.unique>
			<#if i==0>
			String field = ${index.all_d_columnName_get};
			<#assign i=1 />
			<#else>
			field = ${index.all_d_columnName_get};
			</#if>
			if(<#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.containsKey(field))
				throw new SK_Exception(SK_ErrorCode.UNIQUE_INDEX_ERROR,SK_Plus.b(${d_tableName}.<#list index.d_columnNames as d_columnName>${d_columnName}</#list>_IndexName,"=",field).e());
				
			</#if>
			</#list>
		}finally{
			tableLock.unlock();
		}
	}
	
	/** 检查(改变的)唯一索引 */
	private static void checkUniqueIndex(Map<String,String> indexs) throws SK_Exception{
		try{
			tableLock.lock();
			for (Map.Entry<String, String> index : indexs.entrySet()) {
				switch (index.getKey()) {
				<#list indexKeys as index>
				<#if index.unique>
				case ${d_tableName}.<#list index.d_columnNames as d_columnName>${d_columnName}</#list>_IndexName:
					if(<#list index.x_columnNames as x_columnName>${x_columnName}</#list>Cache.containsKey(index.getValue()))
						throw new SK_Exception(SK_ErrorCode.UNIQUE_INDEX_ERROR,SK_Plus.b(${d_tableName}.<#list index.d_columnNames as d_columnName>${d_columnName}</#list>_IndexName,"=",index.getValue()).e());
					break;
				</#if>			
				</#list>
				}
			}
		}finally{
			tableLock.unlock();
		}
	}
	/** 停止数据线程(不能私自调用 )*/
	public static void shutdown(){
	   saveThreadPool.shutdown();
	   try {
	     if (!saveThreadPool.awaitTermination(60, TimeUnit.SECONDS)) {
	       saveThreadPool.shutdownNow();
	       if (!saveThreadPool.awaitTermination(60, TimeUnit.SECONDS))
	           System.err.println("Pool did not terminate");
	     }
	   } catch (InterruptedException ie) {
	     saveThreadPool.shutdownNow();
	     Thread.currentThread().interrupt();
	   }
	}
}